/*
 * Created on May 15, 2006
 *
 */
package ar.edu.utn.frba.gdaia.src.action;

import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.swing.tree.DefaultMutableTreeNode;

import ar.edu.utn.frba.gdaia.src.domain.Canal;
import ar.edu.utn.frba.gdaia.src.domain.Concepto;
import ar.edu.utn.frba.gdaia.src.domain.ConceptoElemental;
import ar.edu.utn.frba.gdaia.src.domain.Regla;
import ar.edu.utn.frba.gdaia.src.domain.Situacion;
import ar.edu.utn.frba.gdaia.src.main.Cerebro;
import ar.edu.utn.frba.gdaia.src.util.GDAIAUtil;
import ar.edu.utn.frba.gdaia.src.util.IntelligentSort;
import ar.edu.utn.frba.gdaia.src.util.ListBuilder;
import ar.edu.utn.frba.gdaia.src.util.SituationComparator;
import ar.edu.utn.frba.gdaia.src.util.TreeUtil;

/**
 * @author Patricio, Fernando Genera una lista de todas las reglas que la
 *         situacion inicial sea exactamente igual a la situacion actual del
 *         cerebro.
 */
public class MakeRuleListCommand extends Command {

	private static Regla regla = null;

	/*
	 * (non-Javadoc)
	 * 
	 * @see ar.edu.utn.frba.gdaia.src.action.Command#execute(java.util.Map)
	 */
	public void execute(Map parameters) {

		// Recupero el cerebro
		Cerebro cerebro = (Cerebro) parameters.get(Command.CEREBRO);

		// Lista de reglas a retornar
		List<Regla> reglasToReturn = new ArrayList<Regla>();
		
		List situacionesPorABS = null;

		// Si no se cumple esta situacion, es porque acabo de aprender una
		// regla, y no tiene sentido hacer una lista de reglas aplicables.
		if (cerebro.getSituacionActual() != null) {

			// En este caso son reglas con situaciones iniciales son iguales a la del cerebro.
			List reglasAplicables = GDAIAUtil.getAllReglasIgualSituacionInicial(cerebro,cerebro.getSituacionActual());
			if(reglasAplicables.isEmpty()){	
				// busco por abstractas
				situacionesPorABS = this.getAllReglasAPartirDeAbstractas(cerebro);
				// las situaciones son las combinaciones por abstracto
				reglasAplicables = GDAIAUtil.getAllReglasIgualSituacionInicial(cerebro, situacionesPorABS);
			}

			if(reglasAplicables.isEmpty()){	
				// busco por instruccion				
				reglasAplicables = this.getAllReglasPorInstruccion(cerebro, situacionesPorABS);
			}

			// reordeno las reglas teniendo en cuenta criterios especiales
			// Ver de modificar esta clase para mejores resultados
			regla = IntelligentSort.sort(reglasAplicables, cerebro.getSituacionActual());
			reglasToReturn.clear();
			reglasToReturn.add(regla);
		}

		// Seteo el retorno en el contexto
		parameters.put(Command.REGLAS_EXTERNAS_TO_RETURN, reglasToReturn);
	}

	/**
	 * LA idea de este metodo es hacer, dada la siguiente situacionde entrada: 
	 * Sit de entrada: [3, a]
	 * Generar: [COMODIN,3], [3,COMODIN]
	 * y armar situaciones para probar
	 * @param cerebro
	 * @param situacionesPorABS 
	 * @param situacionesPorABS 
	 * @return
	 */
	private List getAllReglasPorInstruccion(Cerebro cerebro, List situacionesPorABS) {
		
		Canal canalI=Canal.INT_CONCEPTO_INSTRUCCION_CONCEPTO_CANTIDAD;
		ConceptoElemental conceptoComodin=new ConceptoElemental(10001, Concepto.CONC_INSTR_CANTIDAD,canalI);		
		
		// tengo que crear una a una las combinaciones con conceptos comodines. ahora replico situaciones
		// y luego remplazo uno a uno los conceptos por comodines.
		
		for (Iterator it = situacionesPorABS.iterator(); it.hasNext();) {
			Situacion sit = (Situacion) it.next();			
		
			List estadoActual = sit.getEstado(); 
			for (int i = 0; i < estadoActual.size() ; i++) {
				// hago una copia
				Situacion sitComb = new Situacion(new ListBuilder().add(estadoActual).get(),canalI);
				ConceptoElemental concepto = (ConceptoElemental) sitComb.getEstado().get(i);
				sitComb.getEstado().set(i,conceptoComodin);
				List ret = GDAIAUtil.getAllReglasIgualSituacionInicial(cerebro,sitComb);
				if(!ret.isEmpty()){
					// si encontro algo, vuelvo a reestablecer el conpceto COMODIN por el original
					//((Situacion)ret.get(0)).getEstado().set(i, concepto);
					// retorno la lista candidata.
					return ret;
				}
			}	
		}
		// Si no encontro nada, retorno una lista vacia. VER DE RETORNAR NULL
		return new ArrayList();
	}

	/**
	 * Obtengo las reglas cuya situacion inicial esta formada por combinacion de abstractos
	 * @param cerebro
	 * @return
	 */
	private List getAllReglasAPartirDeAbstractas(Cerebro cerebro) {
				
		// creo el arbol.Esta es la raiz.
		DefaultMutableTreeNode tree = new DefaultMutableTreeNode();				
		
		// construyo el arbol de la combinatoria de los conceptos tomando los posibles abstractos
		this.buildTree(tree,cerebro);
		
		// contruyo lista de situaiones navegando el tree producto de la combinacion de conceptos abstractos
		return this.getSituacionesFromTree(tree);		
		
		//return GDAIAUtil.getAllReglasIgualSituacionInicial(cerebro, situaciones);		
	}
	

	/**
	 * Obtengo los path, que serian las situaciones iniciales posibles.		
	 * con todos los path formados, llego a las hojas y obtengo los path, lease, las combinatorias
	 * de situaciones, con las que tengo que comparar todas las reglas externas.
	 * @param tree
	 * @return una lista de situaciones formadas por los conceptos
	 */
	private List getSituacionesFromTree(DefaultMutableTreeNode tree) {
		List situaciones = new ArrayList();
		
		// obtengo las hojas
		List<DefaultMutableTreeNode> hojas = TreeUtil.obtenerTodasLasHojas(tree);
		
		// debo buscar todos los path desde la raiz hasta las hojas.
		for (Iterator<DefaultMutableTreeNode> it = hojas.iterator(); it.hasNext();) {
			DefaultMutableTreeNode hoja = it.next();
			// obtengo una lista con los cntenidos de los nodos que YO se que son conceptos
			List elementsOfNodes = TreeUtil.getListOfElements(hoja.getPath());
			// remuevo la raiz, es decir, el elemento 0. No me sirve porque no tiene datos			
			elementsOfNodes.remove(0);
			//TODO VER QUE CANAL DE INGRESO PONEMOS A LA SITUACION.
			Situacion situacionSinRoot = new Situacion(elementsOfNodes,Canal.TECLADO);
			situaciones.add(situacionSinRoot);
		}
		
		return situaciones;
	}
	
	
	/**
	 * Construye un arbol con la combinatoria posible de todos los conceptos mas los abstractos
	 * @param tree Resultado de la combinatoria
	 * @param cerebro
	 */
	private void buildTree(DefaultMutableTreeNode tree, Cerebro cerebro) {
		for (Iterator it = cerebro.getSituacionActual().getEstado().iterator(); it.hasNext();) {
			ConceptoElemental concepto = (ConceptoElemental) it.next();
			
			// por cada concepto, una lista de los conceptos abstractos del mismo concepto, incluido el.
			Set abs = this.getAbstractosDelConcepto(concepto);
			abs.add(concepto); // esto es para cuando no encuentra abstractos, que incluya al menos el.
			//TODO un punto de optimizacion es quedarme con las hojas e insertar por las hojas sin buscar todas las veces.
			
			TreeUtil.insertarPorLasHojas(tree,abs);									
		}		
	}

	/**
	 * Retorna los abstractos para un concepto
	 * @param concepto
	 * @return
	 */
	private Set getAbstractosDelConcepto(ConceptoElemental concepto) {
		Set conceptosAbstractos = new HashSet();
		if(!concepto.tienePadres())  
			return conceptosAbstractos;
		
		Set padres = concepto.getPadres();
		for (Iterator it = padres.iterator(); it.hasNext();) {
			ConceptoElemental padre = (ConceptoElemental) it.next();
			// me quedo solo con los hijos que sean elementales
			for (Iterator iter = padre.getHijos().iterator(); iter.hasNext();) {
				ConceptoElemental unHijo = (ConceptoElemental) iter.next();
				if(unHijo.isElemental())
					conceptosAbstractos.add(unHijo);
			}
			
			//TODO ver como hacerlo recursivo. Debe ser transparente para el resto.
		}
		// remuevo el concepto para el cual pedi sus abstractos. Ver AbstraerConceptosCommand.java
		//conceptosAbstractos.remove(concepto);
		return conceptosAbstractos;
	}



	
}
